package com.wiseasy.smartpos.cashierdemo.util;

import androidx.annotation.Keep;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;

@Keep
public class TLV {
    private final static String DIR = "6F,A5,BF0C,70,61,77";
    private final static String LEN1 = "6F,4F";

    private final static HashSet<Integer> dirSet = new HashSet<Integer>();
    private final static HashSet<Integer> len1Set = new HashSet<Integer>();

    static {
        for (String s : DIR.split(",")) {
            int n = Integer.parseInt(s, 16);
            dirSet.add(n);
        }
        for (String s : LEN1.split(",")) {
            int n = Integer.parseInt(s, 16);
            len1Set.add(n);
        }
    }

    public String name;
    public TLV[] subs;

    public static byte[] findByTag(byte[] bs, int tag) {
        return findByTag(bs, 0, bs.length, tag);
    }

    public static byte[] findByTag(byte[] bs, int pos, int len, int tag) {
        int end = pos + len;
        while (pos < end) {
            int t = bs[pos++] & 0xFF;
            if ((t & 0xF) == 0xF && !len1Set.contains(t)) {
                t <<= 8;
                if (pos + 1 < end) {
                    t |= bs[pos++] & 0xFF;
                } else {
                    return null;
                }
            }
            int tlen = 0;
            if (pos < end - 1) {
                tlen = bs[pos++] & 0xFF;
            } else {
                return null;
            }
            if ((tlen & 0x80) != 0) {
                int lenCount = tlen & 0x7F;
                tlen = 0;
                for (int i = 0; i < lenCount; i++) {
                    tlen <<= 8;
                    if (pos < end - 1) {
                        tlen |= bs[pos++] & 0xFF;
                    } else {
                        return null;
                    }
                }
            }
            if (pos + tlen > end) {
                return null;
            }
            if (t == tag) {
                return Arrays.copyOfRange(bs, pos, pos + tlen);
            }
            if (dirSet.contains(t)) {
                byte[] r = findByTag(bs, pos, end - pos, tag);
                if (r != null) {
                    return r;
                }
            } else {
                pos += tlen;
            }
        }
        return null;
    }

    private final static char[] CS = "0123456789ABCDEF".toCharArray();

    private static void appendByte(StringBuilder sb, int n) {
        sb.append(CS[(n >> 4) & 0xF]);
        sb.append(CS[(n >> 0) & 0xF]);
    }

    private static void appendBytes(StringBuilder sb, byte[] bs, int p, int len) {
        for (int i = p; i < p + len; i++) {
            int n = bs[i];
            sb.append(CS[(n >> 4) & 0xF]);
            sb.append(CS[(n >> 0) & 0xF]);
        }
    }

    public static String toString(byte[] bs) {
        StringBuilder sb = new StringBuilder();
        toString(bs, 0, bs.length, sb, "");
        return sb.toString();
    }

    public static void anaTag(byte[] bs, HashMap<String, String> map) {
        TLV.toString(bs);
        anaTag(bs, 0, bs.length, map);
    }

    public static void decodeBF0C(byte[] bs, HashMap<String, String> map) {
        String p4F = null;
        String p50 = null;
        int p87 = 0xFFFF;
        for (int i = 0; i < bs.length; i++) {
            if (bs[i] == 0x61) {
                int len = bs[i + 1] & 0xFF;
                byte[] ts = Arrays.copyOfRange(bs, i + 2, i + 2 + len);
                byte[] fs = findByTag(ts, 0x87);
                if (fs == null || fs.length <= 0 || (fs[0] & 0xFF) < p87) {
                    if (fs == null || fs.length <= 0) {
                        p87 = 0;
                    } else {
                        p87 = fs[0] & 0xFF;
                    }
                    byte[] tbs = findByTag(bs, 0x4F);
                    if (tbs != null) {
                        p4F = bytesToHex(tbs);
                    }
                    tbs = findByTag(bs, 0x50);
                    if (tbs != null) {
                        p50 = bytesToHex(tbs);
                    }
                }

            } else {
                break;
            }
        }
        if (p4F != null) {
            map.put("4F", p4F);
        }
        if (p50 != null) {
            map.put("50", p50);
        }
    }

    public static void anaTag(byte[] bs, int pos, int len, Map<String, String> map) {
        int end = pos + len;
        while (pos < end) {
            int t = bs[pos++] & 0xFF;
            if ((t & 0xF) == 0xF && !len1Set.contains(t)) {
                t <<= 8;
                if (pos + 1 < end) {
                    t |= bs[pos++] & 0xFF;
                } else {
                    return;
                }
            }
            String tag = Integer.toHexString(t).toUpperCase(Locale.getDefault());
            int tlen = 0;
            // if (pos < end - 1) { //TODO houj 最后一个tag长度为0就漏掉了
            if (pos < end) {
                tlen = bs[pos++] & 0xFF;
            } else {
                return;
            }
            if ((tlen & 0x80) != 0) {
                int lenCount = tlen & 0x7F;
                tlen = 0;
                for (int i = 0; i < lenCount; i++) {
                    tlen <<= 8;
                    if (pos < end - 1) {
                        tlen |= bs[pos++] & 0xFF;
                    } else {
                        return;
                    }
                }
            }
            if (pos + tlen > end) {
                return;
            }
            if (dirSet.contains(t)) {
                // anaTag(bs, pos, end - pos, map);
                map.put(tag, bytesToHex(bs, pos, tlen));
            } else {
                map.put(tag, bytesToHex(bs, pos, tlen));
                pos += tlen;
            }

        }
    }

    private final static HashMap<String, String> nameMap = new HashMap<String, String>();

    public static void toString(byte[] bs, int pos, int len, StringBuilder sb, String pre) {
        int end = pos + len;
        while (pos < end) {
            sb.append(pre);
            int t = bs[pos++] & 0xFF;
            int p = sb.length();
            appendByte(sb, t);
            if ((t & 0xF) == 0xF && !len1Set.contains(t)) {
                t <<= 8;
                if (pos + 1 < end) {
                    t |= bs[pos++] & 0xFF;
                    appendByte(sb, t);
                } else {
                    return;
                }
            }
            String name = sb.substring(p);
            System.out.println(name);
            sb.append('[').append(nameMap.get(name)).append(']');
            sb.append(":");
            int tlen = 0;
            if (pos < end - 1) {
                tlen = bs[pos++] & 0xFF;
            } else {
                return;
            }
            if ((tlen & 0x80) != 0) {
                int lenCount = tlen & 0x7F;
                tlen = 0;
                for (int i = 0; i < lenCount; i++) {
                    tlen <<= 8;
                    if (pos < end - 1) {
                        tlen |= bs[pos++] & 0xFF;
                    } else {
                        return;
                    }
                }
            }
            if (pos + tlen > end) {
                return;
            }
            if (dirSet.contains(t)) {
                sb.append("\n");
                toString(bs, pos, end - pos, sb, pre + "  ");
            } else {
                appendBytes(sb, bs, pos, tlen);
                sb.append("\n");
            }
            pos += tlen;
        }
    }

    public static class DolItem {
        public String tag;
        public int len;

        public String toString() {
            return tag + "[" + len + "]";
        }
    }

    public static DolItem[] decodeDOL(byte[] bs) {
        return decodeDOL(bs, 0, bs.length);
    }

    public static DolItem[] decodeDOL(byte[] bs, int pos, int len) {
        ArrayList<DolItem> r = new ArrayList<DolItem>();
        int end = pos + len;
        while (pos < end) {
            int t = bs[pos++] & 0xFF;
            if ((t & 0xF) == 0xF && !len1Set.contains(t)) {
                t <<= 8;
                if (pos < end) {
                    t |= bs[pos++] & 0xFF;
                } else {
                    break;
                }
            }
            int tlen = 0;
            if (pos < end) {
                tlen = bs[pos++] & 0xFF;
            } else {
                break;
            }
            DolItem it = new DolItem();
            it.tag = Integer.toHexString(t).toUpperCase(Locale.getDefault());
            it.len = tlen;
            r.add(it);
        }
        return r.toArray(new DolItem[0]);
    }

    public static boolean haveAllTag(HashMap<String, String> map, String tags) {
        String[] ss = tags.split(",");
        boolean r = true;
        for (String s : ss) {
            if (s.length() <= 0) {
                continue;
            }
            if (map.get(s) == null) {
                System.out.println("all: not found:" + s + " " + nameMap.get(s));
                r = false;
            }
        }
        if (r) {
            System.out.println("===============all have");
        }
        return r;
    }

    private static void appendV(StringBuilder sb, int v) {
        sb.append(CS[(v >> 4) & 0xF]);
        sb.append(CS[(v >> 0) & 0xF]);
    }

    public static String pack(HashMap<String, String> map, String tags) {
        String[] ss = tags.split(",");
        StringBuilder sb = new StringBuilder();
        for (String s : ss) {
            String v = map.get(s);
            if (v == null) {
                System.out.println("not fount tag:" + s + " " + nameMap.get(s));
            } else {
                sb.append(s);
                int len = v.length() / 2;
                if (len >= 0x80) {
                    int count = 0;
                    if (len > 0xFF) {
                        count = 2;
                    } else {
                        count = 1;
                    }
                    appendV(sb, 0x80 | count);
                    while (--count >= 0) {
                        appendV(sb, len >> (count * 8));
                    }
                } else {
                    sb.append(CS[(len >> 4) & 0xF]);
                    sb.append(CS[(len >> 0) & 0xF]);
                }
                sb.append(v);
                System.out.println(s + ":" + v + " " + nameMap.get(s));
            }
        }
        return sb.toString();
    }

    private final static String Z0 = "00000000000000000000000000000000000000000000000000000000000";

    public static String makePol(DolItem[] pols, HashMap<String, String> map) {
        StringBuilder sb = new StringBuilder();
        for (DolItem d : pols) {
            String r = map.get(d.tag);
            System.out.println("POL:" + d.tag + " " + r);
            if (r == null) {
                System.out.println("makePol=====unknow tag=:" + d.tag);
                r = (Z0.substring(0, d.len * 2));
                map.put(d.tag, r);
            }
            if (r.length() != d.len * 2) {
                throw new RuntimeException("tag len err:" + d.len + " " + r);
            }
            System.out.println(d);
            sb.append(r);
            map.put(d.tag, r);
        }
        return sb.toString();
    }

    private static void printBitInfo(String s, String[] ss) {
        byte[] bs = hexToBytes(s);
        int len = Math.min(bs.length * 8, ss.length);
        for (int i = 0; i < len; i++) {
            if ((bs[i >> 3] & (1 << (i & 7))) != 0) {
                System.out.println(i + ":" + ss[i]);
            }
        }
    }

    public static String makeLinkPinOK(String s) {
        s = s.substring(16);
        for (int i = 0; i < s.length() - 3; i += 4) {
            String sub = s.substring(i, i + 4);
            byte[] bs = hexToBytes(sub);
            if ((bs[0] & 0x3F) == 2) {
                return sub + "00";
            }
        }
        return "3F0000";
    }

    private static String fixF55(String f55, String add) {
        int i = f55.indexOf(',');
        if (i < 0) {
            return f55;
        }
        String pre = f55.substring(0, i);
        String end = f55.substring(i + 1);

        HashMap<String, String> mapPre = new HashMap<String, String>();
        HashMap<String, String> mapEnd = new HashMap<String, String>();
        TLV.anaTag(hexToBytes(pre), mapPre);
        TLV.anaTag(hexToBytes(end), mapEnd);

        StringBuilder sb = new StringBuilder();
        for (String s : add.split(",")) {
            String v = mapEnd.get(s);
            if (v != null && !mapPre.containsKey(s)) {
                if (sb.length() > 0) {
                    sb.append(',');
                }
                sb.append(s);
            }
        }
        String radd = TLV.pack(mapEnd, sb.toString());
        return pre + radd + "," + end;
    }

    public static void main(String[] args) {

    }


    private byte[] data;
    private String tag;
    private int length = -1;
    private byte[] value;

    public static TLV fromRawData(byte[] tlData, int tlOffset, byte[] vData, int vOffset)
    {
        int tLen = getTLength(tlData, tlOffset);
        int lLen = getLLength(tlData, tlOffset + tLen);
        int vLen = calcValueLength(tlData, tlOffset + tLen, lLen);
        TLV d = new TLV();
        d.data = merage(new byte[][] { subBytes(tlData, tlOffset, tLen + lLen), subBytes(vData, vOffset, vLen) });
        d.getTag();
        d.getLength();
        d.getBytesValue();

        return d;
    }

    public static TLV fromData(String tagName, byte[] value) {
        byte[] tag = hexString2Bytes(tagName);
        TLV d = new TLV();
        d.data = merage(new byte[][] { tag, makeLengthData2(value.length), value });
        d.tag = tagName;
        d.length = value.length;
        d.value = value;
        return d;
    }
    public static TLV fromData(String tagName, byte[] value1,int length) {
        byte[] value = new byte[length];
        System.arraycopy(value1,0,value,0,length);

        byte[] tag = hexString2Bytes(tagName);
        TLV d = new TLV();
        d.data = merage(new byte[][] { tag, makeLengthData2(length), value });
        d.tag = tagName;
        d.length = length;
        d.value = value;
        return d;
    }
    public static TLV fromRawData(byte[] data, int offset) {
        int len = getDataLength(data, offset);
        TLV d = new TLV();
        d.data = subBytes(data, offset, len);
        d.getTag();
        d.getLength();
        d.getBytesValue();
        return d;
    }

    public String getTag() {
        if (this.tag != null) {
            return this.tag;
        }
        int tLen = getTLength(this.data, 0);
        return this.tag = bytes2HexString(subBytes(this.data, 0, tLen));
    }

    public int getLength() {
        if (this.length > -1) {
            return this.length;
        }
        int offset = getTLength(this.data, 0);
        int l = getLLength(this.data, offset);
        if (l == 1) {
            return this.data[offset] & 0xFF;
        }

        int afterLen = 0;
        for (int i = 1; i < l; i++) {
            afterLen <<= 8;
            afterLen |= this.data[(offset + i)] & 0xFF;
        }
        return this.length = afterLen;
    }

    public int getTLLength() {
        if (this.data == null) {
            return -1;
        }
        return this.data.length - getBytesValue().length;
    }

    public String getValue() {
        byte[] temp = getBytesValue();
        return bytes2HexString(temp == null ? new byte[0] : temp);
    }

    public byte getByteValue() {
        return getBytesValue()[0];
    }

    public String getGBKValue() {
        try {
            return new String(getBytesValue(), "GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getNumberValue() {
        String num = getValue();
        return String.valueOf(Integer.parseInt(num));
    }

    public byte[] getGBKNumberValue() {
        try {
            return getNumberValue().getBytes("GBK");
        } catch (UnsupportedEncodingException localUnsupportedEncodingException) {
        }
        return null;
    }

    public byte[] getBCDValue() {
        return hexString2Bytes(getGBKValue());
    }

    public byte[] getRawData() {
        return this.data;
    }

    public byte[] getBytesValue() {
        if (this.value != null) {
            return this.value;
        }
        int l = getLength();
        return this.value = subBytes(this.data, this.data.length - l, l);
    }

    public boolean isValid() {
        return this.data != null;
    }

    private static int getTLength(byte[] data, int offset) {
        if ((data[offset] & 0x1F) == 31) {
            if ((data[offset+2] & 0x80) == 0x80){
                return 4;
            }else if((data[offset+1] & 0x80) == 0x80){
                return 3;
            }else{
                return 2;
            }
        }
        return 1;
    }

    private static int getLLength(byte[] data, int offset) {
        if ((data[offset] & 0x80) == 0) {
            return 1;
        }
        return (data[offset] & 0x7F) + 1;
    }

    private static int getDataLength(byte[] data, int offset) {
        int tLen = getTLength(data, offset);
        int lLen = getLLength(data, offset + tLen);
        int vLen = calcValueLength(data, offset + tLen, lLen);
        return tLen + lLen + vLen;
    }

    private static int calcValueLength(byte[] l, int offset, int lLen) {
        if (lLen == 1) {
            return l[offset] & 0xFF;
        }

        int vLen = 0;
        for (int i = 1; i < lLen; i++) {
            vLen <<= 8;
            vLen |= l[(offset + i)] & 0xFF;
        }
        return vLen;
    }

    private static byte[] makeLengthData(int len) {
        if (len > 127) {
            byte[] lenData = new byte[4];
            int validIndex = -1;
            for (int i = 0; i < lenData.length; i++) {
                lenData[i] = ((byte)(len >> 8 * (3 - i) & 0xF));
                if ((lenData[(3 - i)] != 0) && (validIndex < 0)) {
                    validIndex = i;
                }
            }
            lenData = subBytes(lenData, validIndex, -1);
            lenData = merage(new byte[][] { { (byte)(0x80 & lenData.length) }, lenData });
            return lenData;
        }

        return new byte[] { (byte)len };
    }

    private static byte[] makeLengthData2(int len) {
        if (len > 127) {
            //转化长度 to byte
            int validIndex = -1;
            byte[] lenData = intToBytes(len);
            for(int i = 0;i<lenData.length;i++){
                if(lenData[i] != 0){
                    //有数字
                    validIndex = i;
                    break;
                }
            }
            byte[] lenresult = new byte[4-validIndex+1];
            byte l = (byte) (4-validIndex);
            lenresult[0] = (byte) (0x80 | l);
            System.arraycopy(lenData,validIndex,lenresult,1,4-validIndex);

            return lenresult;
        }

        return new byte[] { (byte)len };
    }


    public boolean equals(Object obj)
    {
        if (obj == this) {
            return true;
        }

        if (!(obj instanceof TLV)) {
            return false;
        }

        if ((this.data == null) || (((TLV)obj).data == null)) {
            return false;
        }

        return Arrays.equals(this.data, ((TLV)obj).data);
    }

    public String toString()
    {
        if (this.data == null) {
            return super.toString();
        }
        return bytes2HexString(this.data);
    }

    public static byte[] hexToBytes(String s) {
        s = s.toUpperCase();
        int len = s.length() / 2;
        int ii = 0;
        byte[] bs = new byte[len];

        for(int i = 0; i < len; ++i) {
            char c = s.charAt(ii++);
            int h;
            if(c <= 57) {
                h = c - 48;
            } else {
                h = c - 65 + 10;
            }

            h <<= 4;
            c = s.charAt(ii++);
            if(c <= 57) {
                h |= c - 48;
            } else {
                h |= c - 65 + 10;
            }

            bs[i] = (byte)h;
        }

        return bs;
    }


    public static String bytesToHex(byte[] bs) {
        char[] cs = new char[bs.length * 2];
        int io = 0;
        byte[] var6 = bs;
        int var5 = bs.length;

        for(int var4 = 0; var4 < var5; ++var4) {
            byte n = var6[var4];
            cs[io++] = CS[n >> 4 & 15];
            cs[io++] = CS[n >> 0 & 15];
        }

        return new String(cs);
    }

    private static String bytesToHex(byte[] bs, int pos, int len) {
        char[] cs = new char[len * 2];
        int io = 0;

        for(int i = pos; i < pos + len; ++i) {
            byte n = bs[i];
            cs[io++] = CS[n >> 4 & 15];
            cs[io++] = CS[n >> 0 & 15];
        }

        return new String(cs);
    }

    public static String bytes2HexString(byte[] data) {
        return bytesToHex(data);
    }

    public static byte[] intToBytes(int intValue) {
        byte[] bytes = new byte[4];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) ((intValue >> ((3 - i) << 3)) & 0xFF);
        }
        return bytes;
    }

    public static byte[] merage(byte[][] data) {
        int len = 0;
        for (int i = 0; i < data.length; i++) {
            if (data[i] == null) {
                throw new IllegalArgumentException("");
            }
            len += data[i].length;
        }

        byte[] newData = new byte[len];
        len = 0;
        byte[][] arrayOfByte = data;
        int j = data.length;
        for (int i = 0; i < j; i++) {
            byte[] d = arrayOfByte[i];
            System.arraycopy(d, 0, newData, len, d.length);
            len += d.length;
        }
        return newData;
    }

    public static byte[] subBytes(byte[] data, int offset, int len) {
        if ((offset < 0) || (data.length <= offset)) {
            return null;
        }

        if ((len < 0) || (data.length < offset + len)) {
            len = data.length - offset;
        }

        byte[] ret = new byte[len];

        System.arraycopy(data, offset, ret, 0, len);
        return ret;
    }

    public static byte[] hexString2Bytes(String data) {
        if ((data.length() & 1) == 1) {
            data += "0";
        }
        return hexToBytes(data);
    }
}

